home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
prog
/
atari
/
c
/
quarkout.23
/
quarkout.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-07-16
|
49KB
|
1,998 lines
/*
quarkoutfile - beseitigt einige Unschoenheiten in Quark Outfiles.
Copyright (C) 1993,1994 Uwe Ohse
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
For questions contact:
e-mail: (prefered)
uwe@tirka.gun.de
Uwe_Ohse@pb2.maus.de
Uwe Ohse @ ME (inside MausNet)
snail-mail:
Uwe Ohse, Drosselstrasse 2, 47055 Duisburg, Germany
*/
/*
* quarkoutfile - ein Hack, um die durch die Quarkdatenbank bedingten
* fehlenden bzw. fehlerhaften Daten zu ergaenzen.
*
* Bitte beachten:
* #define Quark Quark Version 1.x
*
* Saemtliche Inkompatibilitaeten liegen in der Datenbank der derzeitigen
* Quark begruendet. Diese wird in der kommenden Quark Version II voellig
* erneuert, daher wird Quark II diesen Hack nicht mehr benoetigen.
*
* Hacker's Guide: Dieses Programm ist ein Hack, entstanden aus einer Laune,
* geschrieben von jemandem, der 100%ig wusste, was im Quarktausch so
* ablaeuft. Ich kenne die Zusammenhaenge - schliesslich habe ich weite
* Teile des Quarktauschs programmiert. Das bedeutet leider auch, dass ich
* wohl einiges nicht so gut dokumentiert habe, wie es wuenschenswert waere.
* Wer Teile des Programms verbessert oder es um neue Features ergaenzt
* hat, schicke mir bitte diffs zu (mit Erlaeuterungen).
* Ich formatiere die Sourcen mit:
* indent -gnu -ts4 -i4 -bli0 -bad -bap -ss -bc <source>
*
* Prinzipielle Arbeitsweise der Programms:
* Der Aufbau einer Message im Outfile ist ungefaehr:
* MAUS: Quark
* #message-ID #message-ID
* Vvon Vvon (aber eventuell verkuerzt)
* Ggruppe Ggruppe (ev. laenger als bei MAUS)
* Eeingabezeit Euhrzeit des Einfuegens in die Quark
* Wbetreff Wbetreff (2 Zeichen weniger Platz)
* >Von : blahfasel (uhrzeit wie oben) :Von : blafasel (echte Eingabezeit)
* >sonstige Kopfzeilen :Sonsige Kopfzeilen
* > :
* :Messagetext schliesst sich an. :Messagetext
* Die Quark kennt auch einige Headerzeilen nicht, die die MAUS kennt (IRON).
* Deren Informationen kann man allerdings auch aus den Kopfzeilen
* entnehmen (das sind die mit > beginnenden Zeilen).
* Und genau das tut quarkoutfile.
*
* --------------------------------------------------------------------------
*
* Aenderung von Marcus Endberg:
*
* Anpassung an die lange Betreffzeilen der MAUS-Soft 7.94, die mehr als 30
* Zeichen lange Betreffzeilen erlaubt. Die von mir vorgenommenen Aenderungen
* habe ich im Source gekennzeichnet.
*
* Kontakt: marcus_endberg@pb.maus.de
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <errno.h>
#include <ctype.h>
#include "config.h"
#ifdef HAVE_MMAP
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endif
/* Laenge einer Headerzeile - reicht auch fuer MAUS 9 */
#define HEADLEN 512
/* maximale Laenge einer Textzeile - das reicht allemale */
#define LINESIZE 32767
/* fuer klinisch tote Systeme ohne verlaessliches argc/argv */
char *myname = "quarkoutfile";
#ifdef HAVE_MMAP
caddr_t mmaddr;
size_t mmsize;
int is_mmapped;
#endif
/* Liste zur Unterbringung der Kopfzeilen */
typedef struct kentrystruct
{
unsigned char line[HEADLEN];
struct kentrystruct *next;
} kentry_t;
kentry_t *kanker; /* Pointer auf erste Kopfzeile */
kentry_t *klast; /* Pointer auf letzte Kopfzeile */
typedef struct grpconv_struct
{
char *longname;
char *shortname;
struct grpconv_struct *next;
} grpconv_t;
grpconv_t *glanker; /* Liste, nach longnames sortiert */
grpconv_t *gsanker; /* Liste nach shortnames sort. */
BOOLEAN grplist_changed; /* Hat sich die Liste geaendert? */
grpconv_t *gshash[256]; /* Gruppennamenhashtabelle */
grpconv_t *glhash[256]; /* Gruppennamenhashtabelle */
/*
* Alle chars sind unsigned, weil ein paar daemliche C-Librarys meinen,
* sie duerften sonst Zeichen wegwerfen oder ruinieren. Das betraf
* auch (aeltere) Linux Libs.
*/
static unsigned char l_mausid[HEADLEN]; /* # */
static unsigned char l_mausref[HEADLEN]; /* - */
static unsigned char l_from[HEADLEN]; /* V */
static unsigned char l_to[HEADLEN]; /* A */
static unsigned char l_subject[HEADLEN]; /* W */
static unsigned char l_date[HEADLEN]; /* E */
static unsigned char l_groups[HEADLEN]; /* G */
static unsigned char l_status[HEADLEN]; /* B */
static unsigned char l_realref[HEADLEN]; /* R */
static unsigned char l_organization[HEADLEN]; /* O */
static unsigned char l_name[HEADLEN]; /* N */
static unsigned char l_distribution[HEADLEN]; /* D */
static unsigned char l_realid[HEADLEN]; /* I */
static unsigned char l_gateway[HEADLEN]; /* Y */
static BOOLEAN kill_umlaute; /* Umlaute im Betreff ersetzen */
static BOOLEAN quark16; /* Ist es eine Quark 1.6? */
static BOOLEAN savekopfaskopf; /* >-Zeilen erzeugen? */
static BOOLEAN savekopfastext; /* Oder >-Zeilen als Textzeilen? */
static short showdots = 0; /* Verlaufsanzeige */
static char *fake_tversion; /* Tauschversionsnr in LOG/HEAD faelschen? */
static char *fake_version; /* Versionsnummer in LOG/HEAD faelschen? */
static char *fake_box; /* Boxtyp im LOG/HEAD faelschen? */
static unsigned int limit_subject;/* Betreff kuerzen */
static BOOLEAN headfilt; /* HEAD filtern */
static FILE *dotsfile = stdout; /* Punkte auf stdout (Default) */
static unsigned int columns = 80; /* Zeichen pro BS-Zeile */
static BOOLEAN gconv2long; /* Gruppennamen Kurz->Lang */
static BOOLEAN gconv2short; /* Gruppennamen Lang->Kurz */
static BOOLEAN make_shortnames; /* Falls noetig Gruppennamen kuerzen */
static BOOLEAN long2short_ids; /* Lange IDs in Kurze wandeln */
static BOOLEAN short2long_ids; /* und kurze in lange */
/* Nachrichten mit nichtkonvertierbaren IDs wegwerfen? */
static BOOLEAN discard_false_ids;
static FILE *probsfile = stderr; /* Problemmeldungen in welches File? */
/*
* Nun doch noch: Prototypen.
*/
void clean_header (void);
BOOLEAN write_header (FILE * f);
void * xmalloc (size_t size);
void savekopf (unsigned char *s);
unsigned char * nextline(FILE *in);
void quarkoutfile (FILE * in, FILE * out);
void usage (void);
unsigned char * trim(unsigned char *s);
unsigned char * get_longname(unsigned char *sname);
unsigned char * get_shortname(unsigned char *lname);
void grp_einfuegen(grpconv_t *snew,grpconv_t *lnew);
void speichere_grplist(const char *fname);
void lade_grplist(const char *fname);
short convert_long_maus_id(char *shortidline,char *longidline,
char *eingabezeit);
void
clean_header (void)
{
kentry_t *p;
kentry_t *pnext;
memset (l_mausid, 0, HEADLEN);
memset (l_mausref, 0, HEADLEN);
memset (l_from, 0, HEADLEN);
memset (l_to, 0, HEADLEN);
memset (l_subject, 0, HEADLEN);
memset (l_date, 0, HEADLEN);
memset (l_groups, 0, HEADLEN);
memset (l_status, 0, HEADLEN);
memset (l_realid, 0, HEADLEN);
memset (l_realref, 0, HEADLEN);
memset (l_organization, 0, HEADLEN);
memset (l_name, 0, HEADLEN);
memset (l_distribution, 0, HEADLEN);
memset (l_gateway, 0, HEADLEN);
p = kanker;
while (p)
{
pnext = p->next;
free (p);
p = pnext;
}
kanker = NULL;
klast = NULL;
}
#define MAUSDE ".maus.de"
#define MAUSDE_LEN (sizeof(MAUSDE)-1)
/* Lange Message-IDs in den #- und Minuszeilen konvertieren in kurze
* in den #- und Minuszeilen sowie lange in den I/R-Zeilen.
* Außerdem wird, um die Entstehung von Dupes bei der Vergabe neuer
* langer Message-IDs in der MAUS zu vermeiden, die Eingabezeit
* auf den Inhalt der urspruenglichen langen ID angepaßt. Das sollte
* nicht noetig sein, aber sicher ist sicher.
*/
short
convert_long_maus_id(char *shortidline,char *longidline,char *eingabezeit)
{
char *mausde;
char *at;
char *dot;
char *p;
char c;
at=strchr(shortidline,'@');
if (!at)
return 1; /* Spezialnachricht o.ae. */
dot=strchr(shortidline,'.');
if (!dot || dot>at)
return 0;
c=toupper(dot[1]);
if (!isupper(c))
return 0;
mausde=shortidline+strlen(shortidline)-MAUSDE_LEN;
if (strcasecmp(mausde,MAUSDE))
return 0;
/* .maus.de muss mindestens zwei Zeichen nach dem @ kommen */
if (mausde<at+2)
return 0;
/*
* Das ist eine lange MAUS-ID. Sie hat das Format:
* 199406250722.a10693@du.maus.de
* ^dot ^at^mausde
* Zuerst mal in die Zeile fuer die lange ID kopieren, falls
* erwuenscht. Sinnvollerweise wuerde das *immer* gemacht, aber
* MAUS laeßt Fremdboxen zur Zeit keine I-Zeilen schicken.
*/
if (longidline)
strcpy(longidline,shortidline);
*mausde='\0'; /* .maus.de weg */
/* -> 199406250722.a10693@du */
p=at;
while (*p)
*p++=toupper(*p);
/* -> 199406250722.a10693@DU */
*dot='\0';
if (eingabezeit)
strcpy(shortidline,eingabezeit);
/* -> 199406250722\0a10693@DU */
*shortidline=c;
/* -> A99406250722.a10693@DU */
memmove(shortidline+1,dot+2,strlen(dot+2)+1);
/* -> A10693@DU */
return TRUE; /* gelungen */
}
/*
* Schreibt die Headerdaten in das File.
* liefert TRUE, falls die Nachricht weiterhin geschrieben werden,
* und FALSE, wenn sie Richtung /dev/null wandern soll.
*/
BOOLEAN
write_header (FILE * f)
{
/* Falls keine lange ID vorhanden ist und die Nachricht aus dem
* MausNet ist, dann eine lange ID erzeugen.
*/
if (!l_realid[0] && l_date[0] && strchr (l_mausid,'@'))
{
char *p;
p=strchr(l_mausid,'@');
if (p && !strchr(p,'.'))
{
p=strchr(l_from,'@');
if (p && !strchr(p,'.'))
{
sprintf(l_realid,"%s.%s.maus.de",l_date,l_mausid);
p=l_realid;
while (*p)
*p++=tolower(*p);
}
}
}
#define OUTPUT(x,y) if (y[0]) fprintf(f,"%c%s"CRLF,x,y);
if (long2short_ids)
{
/*
* Lange IDs in kurze wandeln: Ein primitiver USE2MAUS, der
* mit brutaler Gewalt aus langen IDs in der "#"-Zeile kurze
* macht und die lange in die I-Zeile kopiert.
*/
if (!convert_long_maus_id(l_mausid,NULL,l_date))
{
/* konnte nicht konvertiert werden? */
fprintf(probsfile,
"Kann lange ID nicht in kurze konvertieren: %s\n",
l_mausid);
if (discard_false_ids)
return FALSE;
}
if (*l_mausref && !convert_long_maus_id(l_mausref,l_realref,NULL))
{
/* Das ist normal */
strcpy(l_realref,l_mausref);
*l_mausref='\0';
}
OUTPUT ('#', l_mausid);
OUTPUT ('-', l_mausref);
OUTPUT ('R', l_realref);
OUTPUT ('E', l_date);
}
else if (short2long_ids && strchr(l_mausid,'@'))
{
/*
* Kurze IDs verwerfen, um lange einzusetzen.
* Eine lange ID haben wir schon!
*/
if (!strstr(l_realid,MAUSDE) && !*l_gateway)
{
char *p=strchr(l_mausid,'@');
sprintf(l_gateway,"Gateway @ %s",p+1);
}
strcpy(l_mausid,l_realid);
*l_realid='\0';
strcpy(l_mausref,l_realref);
*l_realref='\0';
OUTPUT ('#', l_mausid);
OUTPUT ('-', l_mausref);
OUTPUT ('E', l_date);
}
else
{
OUTPUT ('#', l_mausid);
OUTPUT ('I', l_realid);
OUTPUT ('-', l_mausref);
OUTPUT ('R', l_realref);
OUTPUT ('E', l_date);
}
OUTPUT ('D', l_distribution);
OUTPUT ('Y', l_gateway);
OUTPUT ('O', l_organization);
OUTPUT ('V', l_from);
OUTPUT ('N', l_name);
OUTPUT ('A', l_to);
if (l_subject[0])
{
/* fuer suboptimal programmierte Frontends, die nicht mit langen
* Betreffs umgehen koennen.
*/
if (limit_subject && strlen(l_subject)>limit_subject)
l_subject[limit_subject]='\0';
if (!kill_umlaute)
fprintf(f,"W%s"CRLF,l_subject);
else
{
/* Und das ist fuer ebenfalls suboptimal programmierte
* Frontends, die keine Umlaute im Betreff moegen.
*/
unsigned char *p;
unsigned int count=0;
fputc('W',f);
p=l_subject;
while (*p)
{
switch(*p)
{
/* ### DANGER: Umlaute bei Portierung wandeln! */
case 'ü': fputs("ue",f); count+=2; break;
case 'ö': fputs("oe",f); count+=2; break;
case 'ä': fputs("ae",f); count+=2; break;
case 'Ü': fputs("Ue",f); count+=2; break;
case 'Ö': fputs("Oe",f); count+=2; break;
case 'Ä': fputs("Ae",f); count+=2; break;
case 'ß': fputs("ss",f); count+=2; break;
default: fputc(*p,f); count++; break;
}
p++;
if (limit_subject && count>=limit_subject)
break;
}
fputs(CRLF,f);
}
}
OUTPUT ('G', l_groups);
OUTPUT ('B', l_status);
if ((savekopfaskopf || savekopfastext) && kanker)
{
kentry_t *p;
p = kanker;
while (p)
{
if (savekopfaskopf)
{
OUTPUT ('>', p->line);
}
else
{
OUTPUT (':', p->line);
}
p = p->next;
}
if (savekopfaskopf)
fprintf (f, ">" CRLF);
else
fprintf (f, ":" CRLF);
}
#undef OUTPUT
return TRUE;
}
/*
* sicheres malloc
*/
void *
xmalloc (size_t size)
{
void *p;
p = malloc (size);
if (!p)
{
/* Nichts zu machen, Pech */
fprintf (stderr, "%s: out of memory\n", myname);
exit (1);
}
return p;
}
/*
* Kopfzeile sichern
*/
void
savekopf (unsigned char *s)
{
kentry_t *p;
p = xmalloc (sizeof (kentry_t));
strcpy (p->line, s);
p->next = NULL;
if (!klast)
{
kanker = p;
klast = p;
}
else
{
klast->next = p;
klast = p;
}
}
unsigned char *inputbuffer;
unsigned char *
nextline(FILE *in)
{
unsigned char *p;
#ifdef HAVE_MMAP
if (is_mmapped)
{
unsigned char *ret;
ret=inputbuffer;
if (!ret)
return NULL;
p=strpbrk(inputbuffer,"\r\n");
if (!p)
{
/* Möglicherweise Müll in der Zeile */
/* ASCII-Null *in* der Zeile kommt sehr, sehr schlecht */
while (1)
{
p=inputbuffer+strlen(inputbuffer);
if (p!=(unsigned char *)(mmaddr+mmsize-1))
*p=' ';
else break;
}
p=strpbrk(inputbuffer,"\r\n");
}
if (p)
{
if (*p=='\r')
{
*p++='\0';
*p++='\0';
}
else
*p++='\0';
if (p<=((unsigned char *)mmaddr+mmsize-1))
inputbuffer=p;
else
inputbuffer=NULL;
}
else
inputbuffer=NULL;
if (*ret)
return ret;
return NULL;
}
#endif
if (!fgets (inputbuffer, LINESIZE, in))
return NULL;
p=strpbrk(inputbuffer,"\r\n");
if (p)
*p='\0';
return inputbuffer;
}
/*
* Die Funktion, die die ganze Arbeit erledigt
*/
void
quarkoutfile (FILE * in, FILE * out)
{
unsigned char *line;
unsigned char *p;
BOOLEAN is_header; /* Sind wir im Header einer Nachricht? */
enum
{
KEIN_SPECIAL, /* in normaler Nachricht */
SOME_SPECIAL, /* in irgendeiner Spezialnachricht */
ITG, /* im ITG */
HEAD, /* im HEAD */
LOG /* im LOG */
}
in_special = KEIN_SPECIAL;
int logcmdwritten; /* :#CMD in #LOG eingefuegt? */
int killdoublePdoubleC; /* naechste :#-Zeile loeschen */
int nachrichtennr; /* Nummer der aktuellen Nachricht */
BOOLEAN work_on_it=TRUE; /* Diese Nachricht sichern? */
#ifdef HAVE_MMAP
if (is_mmapped)
inputbuffer = (unsigned char *) mmaddr;
else
#endif
inputbuffer = xmalloc (LINESIZE);
clean_header ();
is_header = FALSE;
logcmdwritten = 0;
killdoublePdoubleC = 0;
nachrichtennr = 0;
/* Solange es noch Daten zu lesen gibt */
while ((line=nextline(in))!=NULL)
{
if (!work_on_it)
{
if (*line!='#')
continue;
work_on_it=TRUE;
}
/* Sind wir in einer Spezialnachricht (LOG oder Ixx)=? */
if (in_special != KEIN_SPECIAL)
{
/*
* Ja. In dem Fall wird die Nachricht einfach kopiert.
* Um Header etc. braucht man sich nicht zu kuemmern.
*/
/* wenn nicht schon die naechste Nachricht beginnt */
if (line[0] != '#')
{
/* Ueber Braindamages bei Frontendprogrammierern
* koennte ich lange lametieren. Lassen wir das besser.
* Zuerst der HEAD-Filter (manche Frontends moegen HEAD
* nicht).
*/
if (headfilt && in_special==HEAD)
continue;
/* Manche fehlerhafte Frontends, so z.B. Cat 2.6, fragen
* die Versionsnummer der Box ab, um festzustellen, ob
* bestimmte Features vorhanden sind. Das ist, da die
* Versionsnummern sich natuerlich bei unterschiedlichen
* Boxtypen unterscheiden, ausgesprochen unguenstig.
* Wer soetwas tut, sollte eigentlich mit Schlaegen oder
* Benutzerflucht bestraft werden.
* Leider wird das aber nicht passieren, und daher bietet
* Quarkoutfile Abhilfe: Es kann den Boxtyp, die
* Boxversionsnummer und die Tauschversionsnummer
* faelschen
*/
if (fake_version || fake_box || fake_tversion)
{
if (in_special == LOG)
{
/* Tauschversionsnummer steht in !V und %T */
if (fake_tversion && !strncmp (line, ":!V", 3))
{
fprintf (out, ":!V%s" CRLF, fake_tversion);
continue; /* naechste Zeile bitte */
}
else if (fake_tversion && !strncmp (line, ":%T", 3))
{
fprintf (out, ":%%T%s" CRLF, fake_tversion);
continue; /* naechste Zeile bitte */
}
/* Boxversionsnummer steht in !M und %V */
if (fake_version && !strncmp (line, ":!M", 3))
{
fprintf (out, ":!M%s" CRLF, fake_version);
continue; /* naechste Zeile bitte */
}
else if (fake_version && !strncmp (line, ":%V", 3))
{
fprintf (out, ":%%V%s" CRLF, fake_version);
continue; /* naechste Zeile bitte */
}
/* Boxtyp in %B */
else if (fake_box && !strncmp (line, ":%B", 3))
{
fprintf (out, ":%%B%s" CRLF, fake_box);
continue; /* naechste Zeile bitte */
}
}
else if (in_special == HEAD)
{
if (fake_version && !strncmp (line, ":V", 3))
{
fprintf (out, ":V%s" CRLF, fake_version);
continue;
}
if (fake_tversion && !strncmp (line, ":T", 3))
{
fprintf (out, ":T%s" CRLF, fake_tversion);
continue;
}
if (fake_box && !strncmp (line, ":B", 3))
{
fprintf (out, ":B%s" CRLF, fake_box);
continue;
}
}
}
/* Soviel zur Versionsnummer, nun zur Konvertierung der
* IDs im LOG. Das gestaltet sich schwieriger, da die
* Quark 1.x die langen IDs nicht im #LOG zurueckmeldet
* (da sie sie auch gar nicht zur MAUS schicken darf und
* von daher nicht weiss, welche ID die MAUS tatsaechlich
* dafuer einsetzt).
*/
if (in_special == LOG && short2long_ids)
{
static char last_gleich[HEADLEN]="";
char c=line[1];
/* dann im LOG IDs konvertieren */
if (c=='"' || c=='#' || c=='$')
{
if (*last_gleich)
{
fprintf(out,"%s"CRLF,last_gleich);
*last_gleich='\0';
}
}
if (!strncmp(line,":=",2))
{
/* sicherheitshalber aufbewahren */
strcpy(last_gleich,line);
continue;
}
if (!strncmp(line,":!=",3))
{
fprintf(out,":=%s"CRLF,line+3);
*last_gleich='\0';
continue;
}
}
/* nun zum Fehlen der #CMD-Bestaetigung in der Quark.
* Braindamage (c) Uwe Ohse.
*/
if (in_special == LOG && !logcmdwritten)
{
if (!strcmp (line, ":#CMD"))
{
fprintf (out, "%s" CRLF, line);
logcmdwritten = 1;
continue;
}
if (line[1] == '\"' || line[1] == '$')
{
fprintf (out, ":#CMD" CRLF);
fprintf (out, ":#" CRLF);
fprintf (out, "%s" CRLF, line);
logcmdwritten = 1;
killdoublePdoubleC = 1; /* muss :# wegwerfen */
continue;
}
}
if (in_special == LOG && killdoublePdoubleC &&
!strcmp (line, ":#"))
{
/* genau diese Zeile loeschen! */
continue;
}
if (in_special == ITG)
{
unsigned char *p;
/* Leerzeichen am Ende der G-Zeilen loeschen */
if (*line == 'G' || (*line == ':' && *(line + 1) == 'G'))
{
unsigned char *c = line + strlen (line) - 1;
unsigned char *compare;
while (*c == ' ')
*c-- = '\0';
if (*line==':')
compare=line+1;
else
compare=line;
if (gconv2short && (p=get_shortname(compare))!=NULL)
{
fprintf (out, ":G%s" CRLF, p);
continue;
}
else if (gconv2long && (p=get_longname(compare))!=NULL)
{
fprintf (out, ":G%s" CRLF, p);
continue;
}
}
if (*line == 'G' || *line == 'U' || *line == 'C' || *line == 'F')
fprintf (out, ":%s" CRLF, line);
else
fprintf (out, "%s" CRLF, line);
continue;
}
fprintf (out, "%s" CRLF, line);
continue;
}
/* Eine neue Nachricht - specialflag zuruecksetzen */
}
/*
* Der eigentliche Parser:
*/
switch (line[0])
{
case '#': /* ID-Zeile, Beginn einer neuen Nachricht */
/*
* Was muss getan werden?
* - Wenn der Header noch nicht gesichert wurde (was definitiv
* bei Statusnachrichten so ist), dann den Header wegschreiben.
* - die Headerdaten muessen geloescht werden.
* - Bei Spezialnachrichten (Ixx,Jxx,LOG etc) Kopiermodus
* einschalten.
* - Wir muessen uns merken, dass wir wieder im Header sind.
* - und natuerlich die ID kopieren!
*/
/* Falls noch im Header (warum auch immer), dann Header sichern */
if (is_header)
(void) write_header (out);
work_on_it=TRUE;
/* alte Headerdaten loeschen */
clean_header ();
in_special = KEIN_SPECIAL;
/* Verlaufsanzeige */
if (showdots && nachrichtennr % showdots == 0)
{
static int dotcount = 0;
fputc ('.', dotsfile);
if (++dotcount % columns == 0)
fprintf (dotsfile, CRLF);
fflush(dotsfile);
}
/* wenn die ID keinen @ enthaelt */
if (!strchr (line + 1, '@'))
{
/*
* dann ist es eine Spezialmitteilung
*/
in_special = SOME_SPECIAL;
if (!strcmp (line, "#ITG"))
in_special = ITG;
if (!strcmp (line, "#HEAD"))
in_special = HEAD;
if (!strcmp (line, "#LOG"))
in_special = LOG;
/* HEAD auf Wunsch filtern */
if (in_special!=HEAD || !headfilt)
fprintf (out, "%s" CRLF, line);
killdoublePdoubleC = 0;
logcmdwritten = 0;
continue;
}
/* ID kopieren */
strcpy (l_mausid, line + 1);
/* wir sind im Header einer Nachricht */
is_header = TRUE;
break;
case '-': /* Verkettungs-ID */
strcpy (l_mausref, line + 1);
break;
case 'V': /* Von */
strcpy (l_from, line + 1);
break;
case 'A': /* an */
strcpy (l_to, line + 1);
break;
case 'Y': /* gateway */
strcpy (l_gateway, line + 1);
break;
case 'W': /* Wegen */
/*
* Die Quark verkuerzt leider auch das Subject um zwei Zeichen.
* Das echte Subject geht leider verloren, und daher kann auch
* quarkoutfile nichts dagegen tun. Bei richtig langen
* Betreffs kommt eine ">Wg. :"-Zeile, aus der man den
* Betreff rekonstruieren kann - aber leider nicht bei
* Betreffs mit 29 und 30 Zeichen. Pech! Quark 1.5i4
* macht es besser :-)
*/
strcpy (l_subject, line + 1);
break;
case 'E': /* Eingabezeit */
/* Bei Quark leider Datum des Einfuegens in die DB */
strcpy (l_date, line + 1);
break;
case 'G': /* Gruppe */
/*
* Die Gruppennamen bei Quarks sind etwas laenger (16 Zeichen),
* und das bringt einige Frontends in leichte Probleme (z.B.
* MAUTAU (considered dead) und auch fruehere Versionen von
* Catputz 2.0x (heutige sind clean).
*/
if (gconv2long && (p=get_longname(line+1))!=NULL)
strcpy(l_groups,p);
else if (gconv2short && (p=get_shortname(line+1))!=NULL)
strcpy(l_groups,p);
else
strcpy (l_groups, line + 1);
break;
case 'B': /* Bearbeitungsstatus */
/*
* Kommt bei Quark 1.x nur in PMs, Statusmeldungen verschickt
* sie nicht.
*/
strcpy (l_status, line + 1);
break;
case 'I': /* ID - die echte ID aus Fremdnetzen */
/*
* Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
* aber dennoch ankommen sollte, merken wir sie uns natuerlich.
*/
strcpy (l_realid, line + 1);
break;
case 'R': /* echte References aus Fremdnetzen */
/*
* Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
* aber dennoch ankommen sollte, merken wir sie uns natuerlich.
*/
strcpy (l_realref, line + 1);
break;
case 'N': /* Realname aus Fremdnetzen */
/*
* Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
* aber dennoch ankommen sollte, merken wir sie uns natuerlich.
*/
strcpy (l_name, line + 1);
break;
case 'O': /* Organization aus Fremdnetzen */
/*
* Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
* aber dennoch ankommen sollte, merken wir sie uns natuerlich.
*/
strcpy (l_organization, line + 1);
break;
case 'D': /* Distribution */
/*
* Die Quark gibt diese Zeile nicht weiter, leider. Falls sie
* aber dennoch ankommen sollte, merken wir sie uns natuerlich.
* btw: Diese Zeile ist bis heute in keiner Tauschdoku
* erwaehnt. Soviel zu deren Aktualitaet.
*/
strcpy (l_distribution, line + 1);
break;
case '>': /* Kopfzeilen */
/*
* Die Quark lieferte diese Zeilen vor Version 1.6 nicht.
* In Quark 1.6 sind sie allerdings nicht brauchbarer als die
* normalen Headerzeilen, deshalb ignoriert Quarkoutfile sie.
*/
savekopf (line + 1);
if (!quark16 && is_header)
{
work_on_it=write_header (out);
is_header = FALSE;
}
break;
case ':': /* Textzeilen */
/*
* hier beginnt die eigentliche Arbeit, das konvertieren
* gewisser Textzeilen.
*/
/* wenn nicht im Header, dann Zeile einfach wegschreiben */
if (!is_header)
{
fprintf (out, "%s" CRLF, line);
break;
}
/*
* So, wir sind definitiv im Header einer Nachricht. Ausserdem
* steht fest, dass es das Outfile einer Quark ist (bei MAUS
* kommen >-Zeilen, die (s.o.) den Header beenden).
* Das heisst, es geht los. Zum Beispiel koennte im Outfile
* stehen:
* :Von : loginname@some.world (Mo, 11.10.93 10:27)
* :Name: Real Name
* :Box : irgendeine site oder organisation
* :MId : <29b8ufINN1ou@some.world>
* :RId : <A27961@someother.world>
* :
* Auf die Reihenfolge der Zeilen verlassen wir uns natuerlich
* nicht.
* Der Parser ist etwas unschoen, da es verschiedene Formen
* fuer die Kopfzeilen gibt. Zum Beispiel tritt ":Von :" als
* als ": Von:" auf.
*/
/* VON-Zeile? */
if (strncmp (line + 1, "Von : ", 6) == 0
|| strncmp (line + 1, " Von: ", 6) == 0)
{
/*
* Zerlege
* :Von : loginname@some.world (Mo, 11.10.93 10:27)
* in "loginname@some.world" -> l_from
* und "Mo, 11.10.93 10:27" -> l_date
* (Anfuehrungszeichen nur zur Erlaeuterung)
* Der Absender wird nach l_from kopiert, da l_from bei
* Quarks ab Zeichen 25 abgeschniten wird. Dies ist
* durch die veraltete Quark-Datenbank bedingt.
*/
unsigned char *uhr;
unsigned char *p;
savekopf (line + 1);
/* Zeile in Absender und Uhrzeit splitten */
uhr = strrchr (line, '(');
if (uhr)
p = strchr (uhr, ')');
if (uhr && p)
{
p = uhr - 1;
*(uhr++) = '\0'; /* '(' weg */
/*
* Die schliessende Klammer und Leerzeichen am Ende des
* Datums weg
*/
p = uhr + strlen (uhr) - 1;
while (*p == ')' || *p == ' ')
*(p--) = '\0';
/*
* Uhrzeit nach l_date uebertragen. Das bedingt eine
* Formatkonvertierung: Aus
* "Mo, 11.10.93 10:27"
* 11111111 }
* 012345678901234567 } Offset
* wird
* "199310111027"
*/
sprintf (l_date, "19%2.2s%2.2s%2.2s%2.2s%2.2s",
uhr + 10, uhr + 7, uhr + 4, uhr + 13, uhr + 16);
} /* if (uhr) (wenn Klammer und damit Uhrzeit gefunden) */
else if (uhr)
*uhr = '\0';
/*
* und nun ueberfluessige Leerzeichen am Ende des
* Absenders weg.
*/
p = line + strlen (line) - 1;
while (*p == ' ')
*(p--) = '\0';
/*
* Jetzt um den Absender kuemmern. Format nun:
* Von : loginname@some.world
* 0123456 == offset
* Problem: Diese Zeile ist in Quark 1.6 ohne @ bei lokalen
* Absendern.
*/
if (!quark16 || strchr (line + 7, '@'))
strcpy (l_from, line + 7);
} /* if "Von : " oder " Von: " am Anfang */
/*
* oder Namens-Zeile?
*/
else if (strncmp (line + 1, "Name: ", 6) == 0)
{
/* einfacher Fall */
savekopf (line + 1);
strcpy (l_name, line + 7);
}
/*
* Oder Organisations/Boxzeile?
*/
else if (strncmp (line + 1, "Box : ", 6) == 0
|| strncmp (line + 1, " Box: ", 6) == 0)
{
savekopf (line + 1);
strcpy (l_organization, line + 7);
}
/*
* echte Message-ID?
*/
else if (strncmp (line + 1, "MId : ", 6) == 0
|| strncmp (line + 1, " MId: ", 6) == 0)
{
/*
* eingehendes Format: "Mid : <irgendwas@irgendwo>"
* In der I-Zeile muss aber "Iirgendwo@irgendwo" stehen,
* also muessen die kleiner/groesser-Zeichen weg.
*/
unsigned char *p;
savekopf (line + 1);
strcpy (l_realid, line + 8); /* <-Zeichen ueberspringen */
p = l_realid + strlen (l_realid) - 1;
if (*p == '>')
*p = '\0';
}
/*
* oder echte (==Fremdnetz-) Message-ID?
*/
else if (strncmp (line + 1, "RId : ", 6) == 0
|| strncmp (line + 1, " RId: ", 6) == 0)
{
/*
* auch hier muessen die kleiner/groesser-Zeichen weg
*/
unsigned char *p;
savekopf (line + 1);
strcpy (l_realref, line + 8);
p = l_realref + strlen (l_realref) - 1;
if (*p == '>')
*p = '\0';
}
/*
* Fido-Misfeature: An-Zeile
*/
else if (strncmp (line + 1, "An : ", 6) == 0
|| strncmp (line + 1, " An: ", 6) == 0)
{
/* Die An:-Zeile ist in Quark 1.6 IMO broken, da sie kein
* @-Zeichen enthaelt.
*/
savekopf (line + 1);
if (!quark16 || strchr (line + 7, '@'))
strcpy (l_to, line + 7);
}
/*
* oder Distributionszeile?
*/
else if (strncmp (line + 1, "Dist: ", 6) == 0)
{
/* Kein savekopf (line + 1), weil "Dist:" nur ein
* Quark-1.5i3-Hack ist.
*/
p = line + 6;
while (isspace (*p))
p++;
if (!*l_distribution)
{
if (!strcasecmp (p, "Net"))
strcpy (l_distribution, "N");
else if (!strcasecmp (p, "MausNet"))
strcpy (l_distribution, "M");
else if (!strcasecmp (p, "Lokal"))
strcpy (l_distribution, "L");
else
strcpy (l_distribution, p);
}
}
/*
* langer Betreff?
*/
else if (strncmp (line + 1, "Wg. : ", 6) == 0
|| strncmp (line + 1, " Wg.: ", 6) == 0)
{
savekopf (line + 1);
strcpy (l_subject, line + 7);
}
/*
* Gatewaykennzeichnung?
*/
else if (strncmp (line + 1, "Gate: ", 6) == 0
|| strncmp (line + 1, "Gate: ", 6) == 0)
{
savekopf (line + 1);
if (!*l_gateway)
strcpy (l_gateway, line + 7);
}
/*
* Alles andere kann nicht mehr zum Header gehoeren.
*/
else
{
work_on_it = write_header (out);
if (line[1] != '\0')
fprintf (out, "%s" CRLF, line);
is_header = FALSE;
}
break;
} /* switch */
} /* while */
if (showdots && nachrichtennr > showdots)
fputc ('\n', dotsfile);
}
void
usage (void)
{
fprintf (stderr, "Benutzung: quarkoutfile [Optionen]\n");
fprintf (stderr,
" Erlaubte Optionen:\n"
" -16 (Outfile von Quark 1.6 konvertieren)\n"
" -b bufsiz (in Kilobyte, fuer Ein- und Ausgabe)\n"
" -B Boxtyp (Boxtyp faelschen, z.B. '-B MAUS')\n"
" -d Msgs/Punkt (Auf 'n' Nachrichten einen Punkt ausgeben\n"
" -F Boxversion (Boxversion faelschen, z.B. '-F 7.94')\n"
" -g gruppen.cnf (Gruppennamen Lang->Kurz)\n"
" -G gruppen.cnf (Gruppennamen Kurz->Lang)\n"
" -I (Infile bearbeiten)\n"
" -h (dieser Text)\n"
" -H (HEAD filtern)\n"
" -K (Kopfzeilen erzeugen)\n"
" -l (Lange IDs in Kurze wandeln (quark2maus)\n"
" -L (Kurze IDs in Lange wandeln (maus2quark)\n"
" -o outputfile (und in diese Schreiben)\n"
" -S Zeichen (Betreff auf 'n' Zeichen kuerzen)\n"
" -T (Textzeilen anstelle der Kopfzeilen)\n"
" -U (Umlaute im Betreff ersetzen)\n"
" -v (Ausgabe der Versionsnummer?)\n"
);
}
unsigned char *
trim(unsigned char *s)
{
unsigned char *p;
while (isspace(*s))
s++;
p=s+strlen(s)-1;
while (isspace(*p))
p--;
p[1]='\0';
return s;
}
/*****************************************************************************
* Gruppennamenskonvertiercode.
* Arbeitet mit zwei alphabetisch sortierten Listen und einem einfachen
* "Hashing", das die Zahl der Suchzugriffe schon mal deutlich senkt, ohne
* allzu viel Logik zu brauchen.
* Beide Listen zeigen auf dieselben Strings!
*****************************************************************************
*/
unsigned char *
get_longname(unsigned char *sname)
{
grpconv_t *akt;
unsigned char c;
c=tolower(*sname);
akt=gshash[c];
if (akt)
{
while (akt && tolower(*akt->shortname)==c)
{
if (!strcasecmp(akt->shortname,sname))
return akt->longname;
akt=akt->next;
}
}
/* nicht gefunden - aber getreu dem dont-trust-my-sources prinzip
* wird doch noch mal die ganze Liste durchsucht.
*/
akt=gsanker;
while (akt)
{
if (!strcasecmp(akt->shortname,sname))
return akt->longname;
akt=akt->next;
}
return NULL;
}
unsigned char *
get_shortname(unsigned char *lname)
{
grpconv_t *akt;
unsigned char c;
c=tolower(*lname);
akt=glhash[c];
if (akt)
{
while (akt && tolower(*akt->longname)==c)
{
if (!strcasecmp(akt->longname,lname))
return akt->shortname;
akt=akt->next;
}
}
/* nicht gefunden - aber getreu dem dont-trust-my-sources prinzip
* wird doch noch mal die ganze Liste durchsucht.
*/
akt=glanker;
while (akt)
{
if (!strcasecmp(akt->longname,lname))
return akt->shortname;
akt=akt->next;
}
/* Nicht gefunden */
if (make_shortnames)
{
char buf[256];
char *p;
char *p1;
size_t l;
size_t l1;
grpconv_t *lnew,*snew;
short war_zu_lang=0;
strcpy(buf,lname);
if (strlen(buf)>10)
war_zu_lang=1;
while ((l=strlen(buf))>10)
{
p=buf;
while (*p)
{
if (*p==' ')
*p='.';
p++;
}
p=buf;
/*
* Nicht toll, aber ausreichend:
* comp.os.linux.announce
* -> com.o.linu.announc
* -> co.o.lin.announ
* -> c.o.lin.annou
* -> c.o.l.anno
*/
l1=l;
while (p && *p)
{
p1=strchr(p,'.');
if (p1==p)
{
memmove(p,p+1,strlen(p));
l--;
}
else if (!p1[1])
{
*p1='\0';
l--;
}
else if (!p1)
{
/* letztes Zeichen weg */
p[strlen(p)-1]='\0';
l--;
p=NULL;
}
else
{
memmove(p1-1,p1,strlen(p1)+1);
l--;
p=p1;
}
if (l<=10)
break;
}
if (l==l1)
{
/* Nichts veraendert - gaanz schlecht! */
p=strchr(buf,'.');
if (!p)
break;
memmove(p,p+1,strlen(p1));
l--;
}
} /* while laenge > 0 */
/* Wenn immer noch zu lang oder nicht eindeutig, dann ... */
if (war_zu_lang && (strlen(buf)>10 || get_longname(buf)))
{
fprintf(stderr,"%s: kann keinen Kurznamen fuer %s generieren\n",
myname,lname);
return NULL;
}
snew=xmalloc(sizeof(grpconv_t));
lnew=xmalloc(sizeof(grpconv_t));
snew->shortname=xmalloc(strlen(buf)+1);
snew->longname=xmalloc(strlen(lname)+1);
strcpy(snew->longname,lname);
strcpy(snew->shortname,buf);
*lnew=*snew;
grp_einfuegen(snew,lnew);
grplist_changed=TRUE;
return buf;
}
return NULL;
}
void
grp_einfuegen(grpconv_t *snew,grpconv_t *lnew)
{
unsigned char c;
grpconv_t *akt,*last;
/*
* Alphabetisch sortiert in die beiden Listen einfuegen, Eintrag in der
* jeweiligen Hashtabelle anlegen.
*/
akt=glanker;
c=tolower(lnew->longname[0]);
last=NULL;
while (akt)
{
if (strcasecmp(akt->longname,lnew->longname)>0)
{
lnew->next=akt;
if (last)
{
last->next=lnew;
/* Hashtabelle updaten */
if (c != tolower(last->longname[0]))
glhash[c]=lnew;
}
else
{
glanker=lnew;
glhash[c]=lnew;
}
break;
}
last=akt;
akt=akt->next;
}
if (!akt)
{
/* nicht gefunden, also kommt also im Alphabet nach allen anderen */
if (last)
{
last->next=lnew;
if (c != tolower(last->longname[0]))
glhash[c]=lnew;
}
else
{
glanker=lnew;
glhash[c]=lnew;
}
lnew->next=NULL;
}
/* und nun dasselbe fuer den kurzen Namen */
akt=gsanker;
c=tolower(snew->shortname[0]);
last=NULL;
while (akt)
{
if (strcasecmp(akt->shortname,snew->shortname)>0)
{
snew->next=akt;
if (last)
{
last->next=snew;
/* Hashtabelle updaten */
if (c != tolower(last->shortname[0]))
gshash[c]=snew;
}
else
{
gsanker=snew;
gshash[c]=snew;
}
break;
}
last=akt;
akt=akt->next;
}
if (!akt)
{
/* nicht gefunden, also kommt also im Alphabet nach allen anderen */
if (last)
{
last->next=snew;
if (c != tolower(last->shortname[0]))
gshash[c]=snew;
}
else
{
gsanker=snew;
gshash[c]=snew;
}
lnew->next=NULL;
}
}
void
speichere_grplist(const char *fname)
{
FILE *f;
grpconv_t *akt;
f=fopen(fname,"w");
if (!f)
{
fprintf(stderr,"%s: kann %s nicht schreiben: %s",
myname,fname,strerror(errno));
exit(1);
}
akt=glanker;
while (akt)
{
fprintf(f,"%s = %s\n",akt->longname,akt->shortname);
akt=akt->next;
}
fclose(f);
}
void
lade_grplist(const char *fname)
{
FILE *f;
unsigned char buf[1024];
unsigned char *p;
unsigned char *lname;
unsigned char *sname;
unsigned int zeile = 0;
grpconv_t *news,*newl;
f=fopen(fname,"r");
if (!f)
{
fprintf(stderr,"%s: kann %s nicht lesen: %s",
myname,fname,strerror(errno));
exit(1);
}
while (fgets(buf,sizeof(buf),f))
{
zeile++;
p=strpbrk(buf,"\r\n");
if (p)
*p='\0';
p=trim(buf);
if (*p==';' || *p=='#' || !*p)
continue;
sname=strchr(buf,'=');
if (!sname)
{
/* Tsss ... */
fprintf(stderr,"%s: Zeile %d in %s nicht parsbar",myname,zeile,
fname);
fprintf(stderr,"%s\n",buf);
continue;
}
*sname++='\0';
lname=trim(buf);
sname=trim(sname);
if (!*lname || !*sname)
{
/* Tsss ... */
fprintf(stderr,"%s: Gruppenname nicht angegeben in %s, Zeile %d\n",
myname,fname,zeile);
continue;
}
news=xmalloc(sizeof(grpconv_t));
newl=xmalloc(sizeof(grpconv_t));
sname=strdup(sname);
lname=strdup(lname);
if (!sname || !lname)
{
fprintf(stderr,"%s: Kein Speicher mehr verfuegbar\n", myname);
exit(1);
}
news->longname=lname;
news->shortname=sname;
newl->longname=lname;
newl->shortname=sname;
grp_einfuegen(news,newl);
}
fclose(f);
}
/*****************************************************************************
* Hauptprogramm: Kommandozeilenauswertung und Dateihandling.
*****************************************************************************
*/
int
main (int argc, char **argv)
{
int i;
char *in_name = NULL;
char *out_name = NULL;
char *grp_name = NULL;
FILE *in = stdin; /* Default */
FILE *out = stdout; /* Default */
int default_buffer = 1; /* Defaultpuffer verwenden */
long buffersize = 0; /* Groess des Puffers in K */
size_t calc_buffersize = 0; /* berechnete wirkliche Puffergroesse */
char tmpfname[PATH_MAX];
char *probsfname = NULL;
int in_place = 0;
char *p;
if (argv[0] && *argv[0])
myname = argv[0];
(void) (argc); /* Keep PureC and BorlandC quiet */
i = 1;
/* getopt kennt zumindest die PureC-Lib nicht */
while (argv[i] != NULL)
{
if (argv[i][0] == '-')
{
switch (argv[i][1])
{
case '0':
discard_false_ids = TRUE;
break;
case '1':
if (argv[i][2] == '6')
quark16 = TRUE;
else
{
fprintf (stderr, "%s: Fuer welche Quark?\n", myname);
exit (2);
}
break;
case 'b':
if (argv[i + 1])
{
default_buffer = 0;
buffersize = atol (argv[++i]);
if (buffersize < 0)
{
fprintf (stderr,
"%s: Puffergroesse muss > 0 sein\n", myname);
exit (2);
}
}
else
{
fprintf (stderr, "%s: Puffergroesse?\n", myname);
exit (2);
}
break;
case 'B':
if (argv[i + 1])
fake_box = argv[++i];
else
{
fprintf (stderr, "%s: Welcher Boxtyp?\n", myname);
exit (2);
}
break;
case 'd':
if (argv[i + 1])
{
showdots = atol (argv[++i]);
if (showdots < 0)
{
fprintf (stderr,
"%s: Msgs/Punkt muss > 0 sein\n",
myname);
exit (2);
}
}
else
{
fprintf (stderr, "%s: buffersize?\n", myname);
exit (2);
}
break;
case 'F':
if (argv[i + 1])
fake_version = argv[++i];
else
{
fprintf (stderr, "%s: Versionsnummer?\n", myname);
exit (2);
}
break;
case 'g':
case 'G':
if (argv[i + 1])
{
/* das jeweils andere Flag loeschen, um bei
* -g -G den Supergau zu vermeiden
*/
if (argv[i][1]=='G')
{
gconv2long=TRUE;
gconv2short=FALSE;
}
else
{
gconv2long=FALSE;
gconv2short=TRUE;
}
grp_name = argv[++i];
}
else
{
fprintf (stderr, "%s: Gruppendateiname?\n", myname);
exit (2);
}
break;
case '?':
case 'h':
usage ();
exit (0);
break;
case 'H':
headfilt=TRUE;
break;
case 'i':
if (argv[i + 1])
in_name = argv[++i];
else
{
fprintf (stderr, "%s: infilename?\n", myname);
exit (2);
}
break;
case 'K':
savekopfaskopf = TRUE;
break;
case 'l':
long2short_ids=TRUE;
short2long_ids=FALSE;
break;
case 'L':
long2short_ids=FALSE;
short2long_ids=TRUE;
break;
case 'o':
if (argv[i + 1])
out_name = argv[++i];
else
{
fprintf (stderr, "%s: outfilename?\n", myname);
exit (2);
}
break;
case 'p':
if (argv[i + 1])
probsfname = argv[++i];
else
{
fprintf (stderr, "%s: Problemfilename?\n", myname);
exit (2);
}
break;
case 's':
make_shortnames = TRUE;
break;
case 'S':
if (argv[i + 1])
limit_subject = (unsigned int) atoi (argv[++i]);
else
{
fprintf (stderr, "%s: Betrefflaenge?\n", myname);
exit (2);
}
break;
case 'T':
savekopfastext = TRUE;
break;
case 'U':
kill_umlaute = TRUE;
break;
case 'v':
fprintf (stderr, "%s version %s\n", myname, VERSIONSTRING);
break; /* kein exit! */
default:
fprintf (stderr, "%s: Option %s ist mir unbekannt\n",
myname, argv[i]);
usage ();
exit (1);
break;
}
}
else
{
fprintf (stderr, "%s: unbekannter Parameter %s\n",
myname, argv[i]);
usage ();
exit (2);
}
i++;
} /* while */
p = getenv ("QUARKVERSION");
if (p)
{
if (!strcmp (p, "16") || !strcmp (p, "1.6"))
quark16 = 1;
}
p=getenv("COLUMNS");
if (p)
{
columns=strtol(p,NULL,10);
if (columns<=0)
columns=80;
}
if (grp_name)
lade_grplist(grp_name);
if (in_name && out_name && !strcmp(in_name,out_name) &&strcmp(in_name,"-"))
{
in_place=1;
/* temporaerdatei im Verzeichnis des Infiles anlegen, das
* spart die Zeit zum Kopieren
*/
/* Ach, ist das schoen, wie vielseitig doch die Dateisysteme
* auf der Welt sind. Nur schade, dass noch nirgendwo das
* kleine e als Trennzeichen verwendet wird :-)
*/
#if defined(__TOS__) || defined(__DOS__) || defined(__OS2__)
p=strrchr(in_name,'\\');
#else
p=strrchr(in_name,'/');
#endif
if (p)
{
strcpy(tmpfname,p+1);
strcat(tmpfname,"qoutfile.tmp");
}
else
strcpy(tmpfname,"qoutfile.tmp");
}
if (!default_buffer)
{
calc_buffersize = (size_t) buffersize *1024;
/* Fuer Systeme mit size_t == 2 Byte - absolut braindamaged */
if (sizeof (size_t) == sizeof (short))
{
if (buffersize * 1024 > USHRT_MAX)
{
fprintf (stderr,
"%s: Puffer zu gross. Verwende Maximum (%ld).\n",
myname, (long) (USHRT_MAX / 1024));
calc_buffersize = USHRT_MAX / 1024;
}
}
}
if (in_name && strcmp(in_name,"-"))
{
#ifdef HAVE_MMAP
int fd;
struct stat st;
fd=open(in_name,O_RDONLY);
if (fd<0)
{
fprintf (stderr, "%s: cannot open %s for reading (%s)\n",
myname, in_name, strerror (errno));
exit (1);
}
if (fstat(fd,&st))
{
fprintf (stderr, "%s: cannot stat %s (%s)\n",
myname, in_name, strerror (errno));
exit (1);
}
mmaddr=mmap(0,st.st_size,
PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
if (mmaddr==(caddr_t) -1)
{
fprintf (stderr, "%s: cannot mmap %s (%s)\n",
myname, in_name, strerror (errno));
in=fdopen(fd,"r");
if (!in)
{
fprintf (stderr, "%s: cannot open %s for reading (%s)\n",
myname, in_name, strerror (errno));
exit (1);
}
mmaddr=(caddr_t)-1;
}
else
{
mmsize=st.st_size;
/* dirty trick: String abschliessen mit \0. Ist eigentlich Schmutz,
* aber als letztes Zeichen *sollte* immer ein \n kommen, und
* darauf kann quarkoutfile auch verzichten.
*/
((char *) mmaddr)[mmsize-1]='\0';
close(fd);
is_mmapped=1;
}
#else
in = fopen (in_name, "r");
if (!in)
{
fprintf (stderr, "%s: cannot open %s for reading (%s)\n",
myname, in_name, strerror (errno));
exit (1);
}
#endif
}
#ifdef HAVE_MMAP
else
{
struct stat st;
mmaddr=(caddr_t) -1;
/* Versuchen wir mal, stdin zu mmappen */
if (fstat(0,&st)==0 && st.st_size)
mmaddr=mmap(0,st.st_size,PROT_READ|PROT_WRITE,MAP_PRIVATE,0,0);
if (mmaddr!=(caddr_t) -1)
{
mmsize=st.st_size;
/* dirty trick: String abschliessen mit \0. Ist eigentlich Schmutz,
* aber als letztes Zeichen *sollte* immer ein \n kommen, und
* darauf kann quarkoutfile auch verzichten.
*/
((char *) mmaddr)[mmsize-1]='\0';
close(0);
is_mmapped=1;
}
else
mmaddr=(caddr_t)-1;
}
#endif
if (out_name && !in_place && strcmp(out_name,"-"))
{
out = fopen (out_name, "w");
if (!out)
{
fprintf (stderr, "%s: cannot open %s for writing (%s)\n",
myname, out_name, strerror (errno));
exit (1);
}
}
if (probsfname)
{
probsfile = fopen (probsfname, "w");
if (!probsfile)
{
fprintf (stderr, "%s: cannot open %s for writing (%s)\n",
myname, probsfname, strerror (errno));
exit (1);
}
/* Die Datei zeilenweise puffern. */
setvbuf (probsfile, NULL, _IOLBF, 0);
}
if (in_place)
{
out = fopen (tmpfname, "w");
if (!out)
{
fprintf (stderr, "%s: kann temporaere Datei %s nicht oeffnen (%s)\n",
myname, tmpfname, strerror (errno));
exit (1);
}
}
if (!default_buffer)
{
if (buffersize == 0)
{
#ifdef HAVE_MMAP
if (!is_mmapped)
#endif
setvbuf (in, NULL, _IONBF, 0); /* ungepuffert */
setvbuf (out, NULL, _IONBF, 0); /* ungepuffert */
}
else
{
/*
* Linux libc (V 4.5.19) laesst bei setvbuf(f,NULL,...)
* fast alles unveraendert. Was soll das?
* Immerhin ist das schon besser als das Verhalten von
* libc (Version 4.4.4), die hat da naemlich auf ungepuffert
* geschaltet ...
*/
char *bin=NULL;
char *bout = malloc (calc_buffersize);
#ifdef HAVE_MMAP
if (!is_mmapped)
bin = malloc (calc_buffersize);
#endif
if (bin && setvbuf (in, bin, _IOFBF, calc_buffersize))
{
/* Fehlschlag, aber das stoert nicht die Funktion des Tools */
fprintf (stderr, "%s: couldn't allocate outputbuffer\n",
myname);
}
if (bout && setvbuf (out, bout, _IOFBF, calc_buffersize))
{
/* Fehlschlag, aber das stoert nicht die Funktion des Tools */
fprintf (stderr, "%s: couldn't allocate outputbuffer\n",
myname);
}
}
}
if (showdots && out == stdout)
{
dotsfile = stderr;
}
quarkoutfile (in, out);
#ifdef HAVE_MMAP
if (is_mmapped)
munmap(mmaddr,mmsize);
#endif
if (grp_name && grplist_changed)
speichere_grplist(grp_name);
if (in_place)
{
/* GEMDOS, langsam und dumm, kann geoeffnete Dateien nicht
* fehlerfrei umbenennen
*/
fclose(out);
if (rename(tmpfname,in_name))
{
/* ? Warum hat das nicht geklappt? */
fprintf (stderr, "%s: kann temporaerdatei nicht umbenennen (%s)\n",
myname,strerror(errno));
exit(1);
}
}
return (0); /* return, not exit, for pure's sake */
}